package com.maoatao.easycodegen.core;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

import java.io.File;
import java.io.FileWriter;
import java.util.Properties;

/**
 * 代码生成器主类
 *
 * @author MaoAtao
 * @date 2025-10-04
 */
public class CodeGenerator {

    /**
     * 生成代码
     *
     * @param config 配置信息
     */
    public void generate(GeneratorConfig config) {
        // 解析DDL
        DdlParser parser = new DdlParser();
        TableInfo tableInfo = parser.parse(config.getDdl(), config);

        // 初始化Velocity引擎
        VelocityEngine velocityEngine = initVelocityEngine();

        // 创建输出目录
        createOutputDirectories(config);

        // 生成各类文件
        generateFiles(velocityEngine, config, tableInfo);
    }

    /**
     * 初始化Velocity引擎
     */
    private VelocityEngine initVelocityEngine() {
        Properties props = new Properties();
        props.setProperty("resource.loader", "class");
        props.setProperty("class.resource.loader.class",
                "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        props.setProperty("runtime.log.logsystem.class", "org.apache.velocity.runtime.log.NullLogChute");

        VelocityEngine velocityEngine = new VelocityEngine();
        velocityEngine.init(props);
        return velocityEngine;
    }

    /**
     * 创建输出目录
     */
    private void createOutputDirectories(GeneratorConfig config) {
        createDirectory(config.getWebOutputPath());
        createDirectory(config.getModelOutputPath());
        createDirectory(config.getServiceOutputPath());
        createDirectory(config.getDaoOutputPath());
    }

    /**
     * 创建目录
     */
    private void createDirectory(String path) {
        if (path != null) {
            File dir = new File(path);
            if (!dir.exists()) {
                dir.mkdirs();
            }
        }
    }

    /**
     * 生成各类文件
     */
    private void generateFiles(VelocityEngine velocityEngine, GeneratorConfig config, TableInfo tableInfo) {
        VelocityContext context = new VelocityContext();
        context.put("table", tableInfo);
        context.put("config", config);
        context.put("packageName", config.getPackageName());
        context.put("categoryName", config.getCategoryName());
        context.put("moduleName", config.getModuleName());
        context.put("authorName", config.getAuthorName());
        context.put("currentDate", java.time.LocalDate.now().toString());
        context.put("hyphenatedInstanceName", toHyphenated(tableInfo.getInstanceName()));
        context.put("pluralInstanceName", toPlural(tableInfo.getInstanceName()));
        context.put("urlPrefix", buildUrlPrefix(config, tableInfo));

        // 生成web层文件
        generateWebFiles(velocityEngine, context, config, tableInfo);

        // 生成model层文件
        generateModelFiles(velocityEngine, context, config, tableInfo);

        // 生成service层文件
        generateServiceFiles(velocityEngine, context, config, tableInfo);

        // 生成dao层文件
        generateDaoFiles(velocityEngine, context, config, tableInfo);
    }

    /**
     * 生成web层文件
     */
    private void generateWebFiles(VelocityEngine velocityEngine, VelocityContext context,
                                  GeneratorConfig config, TableInfo tableInfo) {
        String outputPath = config.getWebOutputPath();
        if (outputPath != null && config.getTemplates().contains(Template.CONTROLLER)) {
            String packagePath = config.getPackageName().replace(".", "/");
            String fullPackagePath = buildPackagePath(outputPath + "/java", packagePath, "web/controller", config.getCategoryName(), config.getModuleName());
            createDirectory(fullPackagePath);

            String fileName = fullPackagePath + "/" + tableInfo.getClassName() + "Controller.java";
            mergeTemplate(velocityEngine, context, "templates/web_controller.vm", fileName);
        }
    }

    /**
     * 生成model层文件
     */
    private void generateModelFiles(VelocityEngine velocityEngine, VelocityContext context,
                                    GeneratorConfig config, TableInfo tableInfo) {
        String outputPath = config.getModelOutputPath();
        if (outputPath != null) {
            String packagePath = config.getPackageName().replace(".", "/");
            String javaPath = outputPath + "/java";

            // 生成BO类
            if (config.getTemplates().contains(Template.MODEL_BO)) {
                String boPath = buildPackagePath(javaPath, packagePath, "model", config.getCategoryName(), config.getModuleName());
                createDirectory(boPath);
                String boFileName = boPath + "/" + tableInfo.getClassName() + "BO.java";
                mergeTemplate(velocityEngine, context, "templates/model_bo.vm", boFileName);
            }

            // 生成Query类
            if (config.getTemplates().contains(Template.MODEL_QUERY)) {
                String queryPath = buildPackagePath(javaPath, packagePath, "model", config.getCategoryName(), config.getModuleName());
                createDirectory(queryPath);
                String queryFileName = queryPath + "/" + tableInfo.getClassName() + "Query.java";
                mergeTemplate(velocityEngine, context, "templates/model_query.vm", queryFileName);
            }

            // 生成Request类
            if (config.getTemplates().contains(Template.MODEL_REQUEST)) {
                String requestPath = buildPackagePath(javaPath, packagePath, "model", config.getCategoryName(), config.getModuleName());
                createDirectory(requestPath);
                String requestFileName = requestPath + "/" + tableInfo.getClassName() + "Request.java";
                mergeTemplate(velocityEngine, context, "templates/model_request.vm", requestFileName);
            }

            // 生成VO类
            if (config.getTemplates().contains(Template.MODEL_VO)) {
                String voPath = buildPackagePath(javaPath, packagePath, "model", config.getCategoryName(), config.getModuleName());
                createDirectory(voPath);
                String voFileName = voPath + "/" + tableInfo.getClassName() + "VO.java";
                mergeTemplate(velocityEngine, context, "templates/model_vo.vm", voFileName);
            }

            // 生成Converter类
            if (config.getTemplates().contains(Template.CONVERTER)) {
                String converterPath = buildPackagePath(javaPath, packagePath, "converter", config.getCategoryName(), config.getModuleName());
                createDirectory(converterPath);
                String converterFileName = converterPath + "/" + tableInfo.getClassName() + "Converter.java";
                mergeTemplate(velocityEngine, context, "templates/converter.vm", converterFileName);
            }
        }
    }

    /**
     * 生成service层文件
     */
    private void generateServiceFiles(VelocityEngine velocityEngine, VelocityContext context,
                                      GeneratorConfig config, TableInfo tableInfo) {
        String outputPath = config.getServiceOutputPath();
        if (outputPath != null) {
            String packagePath = config.getPackageName().replace(".", "/");
            String javaPath = outputPath + "/java";

            // 生成Service接口
            if (config.getTemplates().contains(Template.SERVICE)) {
                String servicePath = buildPackagePath(javaPath, packagePath, "service", config.getCategoryName(), config.getModuleName());
                createDirectory(servicePath);
                String serviceFileName = servicePath + "/" + tableInfo.getClassName() + "Service.java";
                mergeTemplate(velocityEngine, context, "templates/service.vm", serviceFileName);
            }

            // 生成ServiceImpl实现类
            if (config.getTemplates().contains(Template.SERVICE_IMPL)) {
                String serviceImplPath = buildPackagePath(javaPath, packagePath, "service", config.getCategoryName(), config.getModuleName()) + "/impl";
                createDirectory(serviceImplPath);
                String serviceImplFileName = serviceImplPath + "/" + tableInfo.getClassName() + "ServiceImpl.java";
                mergeTemplate(velocityEngine, context, "templates/service_impl.vm", serviceImplFileName);
            }
        }
    }

    /**
     * 生成dao层文件
     */
    private void generateDaoFiles(VelocityEngine velocityEngine, VelocityContext context,
                                  GeneratorConfig config, TableInfo tableInfo) {
        String outputPath = config.getDaoOutputPath();
        if (outputPath != null) {
            String packagePath = config.getPackageName().replace(".", "/");
            String javaPath = outputPath + "/java";

            // 生成Mapper接口
            if (config.getTemplates().contains(Template.MAPPER)) {
                String mapperPath = buildPackagePath(javaPath, packagePath, "mapper", config.getCategoryName(), config.getModuleName());
                createDirectory(mapperPath);
                String mapperFileName = mapperPath + "/" + tableInfo.getClassName() + "Mapper.java";
                mergeTemplate(velocityEngine, context, "templates/mapper.vm", mapperFileName);
            }

            // 生成Mapper XML文件
            if (config.getTemplates().contains(Template.MAPPER_XML)) {
                String resourcesPath = outputPath + "/resources";
                String mapperXmlPath = buildPackagePath(resourcesPath, "", "mapper", config.getCategoryName(), config.getModuleName());
                createDirectory(mapperXmlPath);
                String mapperXmlFileName = mapperXmlPath + "/" + tableInfo.getClassName() + "Mapper.xml";
                mergeTemplate(velocityEngine, context, "templates/mapper.xml.vm", mapperXmlFileName);
            }

            // 生成DO类
            if (config.getTemplates().contains(Template.MODEL_DO)) {
                String doPath = buildPackagePath(javaPath, packagePath, "model", config.getCategoryName(), config.getModuleName());
                createDirectory(doPath);
                String doFileName = doPath + "/" + tableInfo.getClassName() + "DO.java";
                mergeTemplate(velocityEngine, context, "templates/model_do.vm", doFileName);
            }
        }
    }

    /**
     * 将驼峰命名转换为连字符命名
     * 例如：userInfo -> user-info
     *
     * @param camelCaseStr 驼峰命名字符串
     * @return 连字符命名字符串
     */
    private String toHyphenated(String camelCaseStr) {
        if (camelCaseStr == null || camelCaseStr.isEmpty()) {
            return camelCaseStr;
        }

        StringBuilder result = new StringBuilder();
        for (int i = 0; i < camelCaseStr.length(); i++) {
            char c = camelCaseStr.charAt(i);
            if (Character.isUpperCase(c) && i > 0) {
                result.append('-');
            }
            result.append(Character.toLowerCase(c));
        }
        return result.toString();
    }

    /**
     * 将驼峰命名转换为复数形式
     * 例如：userInfo -> userInfo s
     *
     * @param camelCaseStr 驼峰命名字符串
     * @return 复数形式字符串
     */
    private String toPlural(String camelCaseStr) {
        if (camelCaseStr == null || camelCaseStr.isEmpty()) {
            return camelCaseStr;
        }

        // 先转换为连字符格式，再处理复数
        String hyphenated = toHyphenated(camelCaseStr);
        String[] parts = hyphenated.split("-");
        StringBuilder result = new StringBuilder();

        for (int i = 0; i < parts.length; i++) {
            if (i > 0) {
                result.append("-");
            }
            // 只对最后一个单词进行复数处理
            if (i == parts.length - 1) {
                String lastPart = parts[i];
                if (lastPart.endsWith("y")) {
                    result.append(lastPart, 0, lastPart.length() - 1).append("ies");
                } else if (lastPart.endsWith("s") || lastPart.endsWith("sh") || lastPart.endsWith("ch")) {
                    result.append(lastPart).append("es");
                } else {
                    result.append(lastPart).append("s");
                }
            } else {
                result.append(parts[i]);
            }
        }

        return result.toString();
    }

    /**
     * 构建URL前缀
     * 根据配置的URL风格生成不同的URL前缀
     *
     * @param config    配置信息
     * @param tableInfo 表信息
     * @return URL前缀
     */
    private String buildUrlPrefix(GeneratorConfig config, TableInfo tableInfo) {
        String moduleName = config.getModuleName();
        String instanceName = tableInfo.getInstanceName();

        StringBuilder urlPrefix = new StringBuilder("/api");

        // 添加模块名（如果存在）
        if (moduleName != null && !moduleName.isEmpty()) {
            urlPrefix.append("/").append(moduleName);
        }

        // 根据URL风格添加实例名部分
        switch (config.getUrlStyle()) {
            case GRADATION:
                // 生成层级风格URL，如 /api/test/user/infos
                String[] parts = toHyphenated(instanceName).split("-");
                for (int i = 0; i < parts.length; i++) {
                    urlPrefix.append("/").append(parts[i]);
                    // 对最后一个部分进行复数处理
                    if (i == parts.length - 1) {
                        urlPrefix.append(getPluralSuffix(parts[i]));
                    }
                }
                break;
            case CAMEL_CASE:
                // 生成驼峰风格URL，如 /api/test/userInfos
                urlPrefix.append("/").append(instanceName).append("s");
                break;
            case HYPHENATED:
            default:
                // 生成连字符风格URL，如 /api/test/user-infos
                urlPrefix.append("/").append(toHyphenated(instanceName)).append("s");
                break;
        }

        return urlPrefix.toString();
    }

    /**
     * 获取单词的复数后缀
     *
     * @param word 单词
     * @return 复数后缀
     */
    private String getPluralSuffix(String word) {
        if (word == null || word.isEmpty()) {
            return "s";
        }

        // 特殊复数形式
        String lowerWord = word.toLowerCase();
        if (lowerWord.endsWith("y") && isConsonant(word.charAt(word.length() - 2))) {
            // 以辅音字母+y结尾，变y为ies
            return "ies";
        } else if (lowerWord.endsWith("s") || lowerWord.endsWith("sh") ||
                lowerWord.endsWith("ch") || lowerWord.endsWith("x") ||
                lowerWord.endsWith("z") || lowerWord.endsWith("o")) {
            // 以s, sh, ch, x, z, o结尾，加es
            return "es";
        } else {
            // 一般情况加s
            return "s";
        }
    }

    /**
     * 判断字符是否为辅音字母
     *
     * @param c 字符
     * @return 是否为辅音字母
     */
    private boolean isConsonant(char c) {
        char lower = Character.toLowerCase(c);
        return lower != 'a' && lower != 'e' && lower != 'i' && lower != 'o' && lower != 'u';
    }

    /**
     * 构建包路径
     */
    private String buildPackagePath(String javaPath, String packagePath, String layer, String categoryName, String moduleName) {
        StringBuilder pathBuilder = new StringBuilder();
        pathBuilder.append(javaPath).append("/").append(packagePath).append("/").append(layer);

        // 构建中间路径
        String middlePath = buildMiddlePath(categoryName, moduleName);
        if (!middlePath.isEmpty()) {
            pathBuilder.append("/").append(middlePath);
        }

        return pathBuilder.toString();
    }

    /**
     * 构建中间路径
     */
    private String buildMiddlePath(String categoryName, String moduleName) {
        StringBuilder pathBuilder = new StringBuilder();

        // 如果分类名不为空，则添加分类名
        if (categoryName != null && !categoryName.isEmpty()) {
            pathBuilder.append(categoryName);
        }

        // 如果模块名不为空，则添加模块名
        if (moduleName != null && !moduleName.isEmpty()) {
            if (pathBuilder.length() > 0) {
                pathBuilder.append("/");
            }
            pathBuilder.append(moduleName);
        }

        return pathBuilder.toString();
    }

    /**
     * 合并模板和数据生成文件
     */
    private void mergeTemplate(VelocityEngine velocityEngine, VelocityContext context,
                               String templatePath, String outputFile) {
        try {
            org.apache.velocity.Template template = velocityEngine.getTemplate(templatePath, "UTF-8");
            FileWriter writer = new FileWriter(outputFile);
            template.merge(context, writer);
            writer.close();
        } catch (Exception e) {
            throw new RuntimeException("生成文件失败: " + outputFile, e);
        }
    }
}
